#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <string>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/wait.h>

using std::string;
#define SERVER_VER "1.0.0.1"

// 使用backtrace/backtrace_symbols将崩溃点附近的符号打印出来，写到文件中。
// 文件位置目前是固定的，在/tmp/fddsocks5_core目录下。全路径如：/tmp/fddsocks5_core/fddsocks5.core.2024-1-11_7_15_22
void crash_handler(int signo) {
    std::string core_dump_file = "/tmp/fddsocks5_core";
    mkdir(core_dump_file.c_str(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    //创建core文件
    time_t dumpTime;
    time(&dumpTime);
    struct tm xtm;
    struct tm* ptm = &xtm;
    localtime_r(&dumpTime, ptm);
    char core_name[256] = {0};
    sprintf(core_name, "fddsocks5.core.%d-%d-%d_%d_%d_%d",
        ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    core_dump_file += "/";
    core_dump_file += core_name;
    int bf = open(core_dump_file.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRWXU);
    if (bf < 0)
    {
        bf = fileno(stderr);
    }

    char buffer[4096];
    //VERSION ADN TIME.
    memset(buffer, 0, sizeof(buffer));
    int len = sprintf(buffer, "version:%s build:%s %s Dump Time: %d-%d-%d %d:%d:%d\n",
        SERVER_VER, __DATE__, __TIME__,
        ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    write(bf, buffer, len);

    // THREAD AND SIGNAL.
    len = sprintf(buffer, "Curr thread: %u, Catch signal:%d\n",
        (int)pthread_self(), signo);
    write(bf, buffer, len);

    // STACK.
    len = sprintf(buffer, "Core dump, stack trace:\n\n");
    write(bf, buffer, len);

    void* func[1024];
    signal(signo, SIG_DFL);
    size_t size = backtrace(func, 1024);
    if (size > 0)
    {
        char** symbols = backtrace_symbols(func, size);
        if (symbols != NULL)
        {
            for (size_t i = 0; i < size; ++i)
            {
                len = sprintf(buffer, "%u %s\n", (unsigned int)i, symbols[i]);
                write(bf, buffer, len);
            }
            free(symbols);
        }
    }

    close(bf);
    //@TODO: upload file to server
    //e.g. Using CURL upload.

}

void sig_child_hander(int sig)
{
    int save_errno = errno;
    pid_t child_pid;
    int status;
    while ((child_pid = waitpid(-1, &status, WNOHANG)) > 0)
    {
        ;
    }
    errno = save_errno;
}

//int set_resource_limit(int type, int setlimit) {
//    struct rlimit rlim_new;
//    rlim_new.rlim_cur = rlim_new.rlim_max = setlimit;
//    return setrlimit(type, &rlim_new);
//}

//int set_limit(int type, rlim_t limit) {
//    if (set_resource_limit(type, limit) == 0) {
//        return 0;
//    } else {
//        struct rlimit rlim;
//        if (getrlimit(type, &rlim) < 0) {
//            return -1;
//        }
//        return set_resource_limit(type, rlim.rlim_max);
//    }
//}

void run_crash_handler() {
//    设置允许打开的文件数量上限。/proc/sys/fs/nr_open是系统默认值，在ubuntu 20.4中，这个值是1048576。
//    我们使用系统默认的就可以，无需调整。
//    int file_max = 4096;
//    int fd = open("/proc/sys/fs/nr_open", O_RDONLY);
//    if (fd > 0) {
//        char buf[64];
//        if (read(fd, buf, 64) > 0) {
//            file_max = atoi(buf);
//        }
//        close(fd);
//    }
//    set_limit(RLIMIT_NOFILE, file_max);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSEGV, crash_handler);
    signal(SIGABRT, crash_handler);
    signal(SIGCHLD, sig_child_hander);
}


